package com.wyix.aop;

import com.wyix.annotation.LogAnnotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

/**
 * @author wangdx
 * @since 2025/8/6
 */
@Aspect
@Component
public class BookLogAspect {
    // 切点1：匹配service包下所有类的所有方法（基础包路径切点）
    @Pointcut("execution(* com.wyix.service.impl.*.*(..))")
    public void servicePointcut() {}

    // 切点2：匹配带@LogAnnotation注解的方法（自定义注解切点）
    @Pointcut("@annotation(com.wyix.annotation.LogAnnotation)")
    public void annotationPointcut() {}

    // 切点3：匹配返回值为String类型的方法（返回值切点）
    @Pointcut("execution(String com.wyix.service.impl.*.*(..))")
    public void stringReturnPointcut() {}

    // 前置通知：记录方法调用前的信息（参数、时间）
    @Before("servicePointcut()")
    public void beforeLog(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
        String args = Arrays.stream(joinPoint.getArgs())
                .map(arg -> arg == null ? "null" : arg.toString())
                .collect(java.util.stream.Collectors.joining(", "));
        System.out.printf("[前置通知] 时间：%s 方法：%s 开始执行，参数：%s\n", new Date(), methodName, args);
    }

    // 后置通知：记录方法返回值（注意：异常时不触发）
    @AfterReturning(pointcut = "servicePointcut()", returning = "result")
    public void afterReturningLog(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.printf("[后置通知] 方法：%s 执行完成，返回值：%s\n", methodName, result == null ? "无返回值" : result.toString());
    }

    // 环绕通知：统计方法执行耗时（覆盖自定义注解的方法）
    @Around("annotationPointcut()")
    public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();  // 执行目标方法
        long cost = System.currentTimeMillis() - start;
        LogAnnotation annotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(LogAnnotation.class);
        System.out.printf("[环绕通知] 功能：%s 执行耗时：%dms\n", annotation.value(), cost);
        return result;
    }

    // 异常通知：记录方法异常信息（仅当方法抛出RuntimeException时触发）
    @AfterThrowing(pointcut = "servicePointcut()", throwing = "ex", argNames = "joinPoint,ex")
    public void afterThrowingLog(JoinPoint joinPoint, RuntimeException ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.printf("[异常通知] 方法：%s 发生异常：%s\n", methodName, ex.getMessage());
    }

    // 最终通知：无论方法是否异常都会执行（类似finally）
    @After("stringReturnPointcut()")
    public void afterLog(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.printf("[最终通知] 方法：%s 执行结束（返回值为String类型）\n", methodName);
    }
}
